﻿using Micro.Modules;
using Micro.Modules.Abstractions;
using Micro.Utils;
using Micro.Utils.Constants;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Micro.Modules.Core
{

    /// <summary>
    /// 模块集合的默认实现
    /// </summary>
    public class ModuleCollection : List<IModule>, IModuleCollection
    {
        private IServiceCollection _services;
        private readonly ILogger<ModuleCollection> _logger;
        public ModuleCollection(IServiceCollection services)
        {
            _logger = services.BuildServiceProvider().GetService<ILogger<ModuleCollection>>()!;
            //_logger = serviceProvider.GetService<ILoggerFactory>().CreateLogger<ModuleCollection>();
            _services = services;
        }


        public IModule GetById(string id)
        {
            return this.FirstOrDefault(m => m.PluginId == id)!;
        }

        public IModule GetByCode(string code)
        {
            return this.FirstOrDefault(m => m.Code.EqualsIgnoreCase(code))!;
        }

        public async Task Load()
        {
            this.Clear();
            _logger.LogInformation("开始读取插件");

            var modulesRootDirPath = ModuleConstant.ROOT_DIR;// Path.Combine(AppContext.BaseDirectory, "_modules");
            if (!Directory.Exists(modulesRootDirPath))
            {
                _logger.LogInformation($"不存在插件文件夹：{modulesRootDirPath}");
                return;
            }

            var modulesRootDir = new DirectoryInfo(modulesRootDirPath);
            var moduleDirs = modulesRootDir.GetDirectories();
            if (!moduleDirs.Any())
            {
                _logger.LogInformation($"空文件夹：{modulesRootDirPath}");
                return;
            }
            foreach (var moduleDir in moduleDirs)
            {
                var (b, moduleInfo, error) = await AddModule(moduleDir);
                if (b)
                    _services.AddSingleton(moduleInfo);
            }

        }

        public async Task<(bool, IModule, string)> AddModule(string path)
        {
            ModuleInfo moduleInfo = null;
            string error = string.Empty;
            var moduleDir = new DirectoryInfo(path);
            if (moduleDir == null)
            {
                error = $"AddModule空文件夹：{moduleDir}";
                _logger.LogInformation(error);
                return (false, moduleInfo, error);
            }

            return await AddModule(moduleDir);
        }

        public async Task<(bool, IModule, string)> AddModule(DirectoryInfo moduleDir)
        {
            IModule moduleInfo = null;
            string error = string.Empty;


            var moduleJosn = ModuleConstant.JSON_FILE_NAME;
            //从_module.json文件中读取模块信息
            var jsonPath = Path.Combine(moduleDir.FullName, moduleJosn);
            if (!File.Exists(jsonPath))
            {
                error = $"AddModule不存在{moduleJosn}：{jsonPath}";
                _logger.LogInformation(error);
                return (false, moduleInfo, error);
            }

            using (var jsonReader = new StreamReader(jsonPath, Encoding.Default))
            {
                _logger.LogInformation($"加载插件,路径{moduleDir}");
                moduleInfo = await LoadModules(moduleDir, jsonReader);
            }
            return (moduleInfo != null, moduleInfo, error)!;
        }


        private async Task<IModule> LoadModules(DirectoryInfo dir, StreamReader jsonReader)
        {
            var data = jsonReader.ReadToEnd();
            var moduleDescriptor = JsonConvert.DeserializeObject<ModuleInfo>(data);
            if (moduleDescriptor != null)
            {
                moduleDescriptor.ModulePath = dir.FullName;
                //判断是否已存在
                if (!this.Any(m => m.Code.Equals(moduleDescriptor.Code)))
                {
                    //加载程序集
                    await LoadAssemblyDescriptor(moduleDescriptor, Path.Combine(dir.FullName, ModuleConstant.Module_Bin));

                    Add(moduleDescriptor);
                }
                else
                    return this.FirstOrDefault(m => m.Code.Equals(moduleDescriptor.Code))!;
            }
            return moduleDescriptor!;
        }


        private async Task LoadAssemblyDescriptor(ModuleInfo moduleDescriptor, string dir)
        {
            await Task.Run(() =>
            {
                var context = new ModuleAssemblyLoadContext(dir, moduleDescriptor.Code, true);
                moduleDescriptor.Context = context;

                var prefix = ModuleConstant.PREFIX.IsNull()?"":$"{ModuleConstant.PREFIX}.";
                string jobsName = $"{prefix}{string.Format(ModuleConstant.JobsDLLName, moduleDescriptor.Code)}";
                string webName = $"{prefix}{string.Format(ModuleConstant.WebDLLName, moduleDescriptor.Code)}";
                string apiName = $"{prefix}{string.Format(ModuleConstant.ApiDLLName, moduleDescriptor.Code)}";

                var assemble = new ModuleRuntime
                {
                    Jobs = moduleDescriptor.Context.Get(jobsName) ?? AssemblyHelper.LoadByNameEndString(jobsName),
                    Web = moduleDescriptor.Context.Get(webName) ?? AssemblyHelper.LoadByNameEndString(webName),
                    Api = moduleDescriptor.Context.Get(apiName) ?? AssemblyHelper.LoadByNameEndString(apiName),
                };
                //assemble.Main = assemble.Web ?? assemble.Api;
                moduleDescriptor.ModuleRuntime = assemble;
                //加载模块初始化器
                var controllerAssembly = assemble.Web ?? assemble.Api;
                if (controllerAssembly != null)
                {
                    var type = typeof(BaseModule);
                    var initializerType = controllerAssembly.GetTypes().FirstOrDefault(t => type.IsAssignableFrom(t));
                    if (initializerType != null && (initializerType != typeof(BaseModule)))
                    {
                        moduleDescriptor.Initializer = (BaseModule)Activator.CreateInstance(initializerType)!;
                    }
                }
            });
        }
    }
}
